﻿using BencodeNET.Objects;
using BencodeNET.Parsing;
using BencodeNET.Torrents;
using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DependencyInjection;
using Furion.FriendlyException;
using Furion.RemoteRequest.Extensions;
using Mapster;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OnceMi.AspNetCore.OSS;
using PrivateTracker.Service.Core;
using System.Text;
using System.Web;
using Torrent = PrivateTracker.Service.Core.Torrent;

namespace PrivateTracker.Service.Application;
/// <summary>
/// 
/// </summary>
public class TorrentService : ITorrentService, ITransient
{
    private const string bucketName = "bucketName";
    private readonly IRepository<Torrent> _torrentsRepository;
    private readonly ISysFileService _sysFileService;
    private readonly IUserManager _userManager;
    private readonly IOSSServiceFactory _oSSServiceFactory;
    private readonly IHttpContextAccessor _httpContextAccessor;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="torrentsRepository"></param>
    /// <param name="sysFileService"></param>
    /// <param name="userManager"></param>
    /// <param name="oSSServiceFactory"></param>
    /// <param name="httpContextAccessor"></param>
    public TorrentService(IRepository<Torrent> torrentsRepository, ISysFileService sysFileService, IUserManager userManager, IOSSServiceFactory oSSServiceFactory, IHttpContextAccessor httpContextAccessor)
    {
        _torrentsRepository = torrentsRepository;
        _sysFileService = sysFileService;
        _oSSServiceFactory = oSSServiceFactory;
        _userManager = userManager;
        _httpContextAccessor = httpContextAccessor;
    }
    /// <summary>
    /// 添加种子信息
    /// </summary>
    /// <returns></returns>
    public async Task<TorrentOutput> AddAsync(TorrentInput torrentInput)
    {
        try
        {
            var stream = torrentInput.TorrentFile.OpenReadStream();
            var torrent = new BencodeParser().Parse<BencodeNET.Torrents.Torrent>(stream);

            torrent.Comment = "资源来自 PrivateTracker";
            torrent.CreatedBy = "PrivateTracker";
            torrent.ExtraFields = new BDictionary { { TorrentFields.Info, new BDictionary { { "source", "[www.test.net] PrivateTracker" } } } };//TODO 来源站

            string infoHash = torrent.GetInfoHash();
            //资源是否存在
            var item = await _torrentsRepository.FirstOrDefaultAsync(p => p.HashInfo == infoHash);

            if (null != item) throw Oops.Oh("种子资源已存在");
            var title = string.IsNullOrWhiteSpace(torrentInput.Title) ? torrent.DisplayName : torrentInput.Title;
            long fileId = await _sysFileService.UploadTorrentFile(torrentInput.TorrentFile, torrent, title);
            var entityEntry = await new Torrent
            {
                Title = title,
                Subtitle = string.IsNullOrWhiteSpace(torrentInput.Subtitle) ? torrent.DisplayName : torrentInput.Title,
                HashInfo = infoHash,
                TotalSize = torrent.TotalSize,
                FileId = fileId
            }.InsertNowAsync();

            ////https://www.omdbapi.com/?i=tt0079501&&apikey=65f7c6ca


            return await Task.FromResult(entityEntry.Entity.Adapt<TorrentOutput>());
        }
        catch (Exception)
        {

            throw;
        }
    }

    /// <summary>
    /// 删除种子
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task DeleteAsync(DeleteTorrentInput input)
    {
        var user = _userManager.User;
        var userId = long.Parse(App.User?.FindFirst(ClaimConst.CLAINM_USERID)?.Value);

        //资源是否存在
        var item = await _torrentsRepository.DetachedEntities.FirstOrDefaultAsync(p => p.Id == input.Id);
        if (null == item) throw Oops.Oh(ErrorCode.D1002);

        var RoleType = _httpContextAccessor.HttpContext.User.FindFirst(ClaimConst.CLAINM_ROLETYPE)?.Value;
        if (item.CreatedUserId != userId && RoleType == ((int)RoleTypeEnum.LEVEL_GROUP).ToString()) throw Oops.Oh(ErrorCode.D1013);

        await item.DeleteAsync();
        await _sysFileService.DeleteFileInfo(new DeleteFileInfoInput { Id = item.FileId });
    }
    /// <summary>
    /// 获取资源信息
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    public async Task<TorrentOutput> GetAsync(long id)
    {
        var torrent = await _torrentsRepository.FirstOrDefaultAsync(u => u.Id == id);
        return await Task.FromResult(torrent.Adapt<TorrentOutput>());
    }
    /// <summary>
    /// 获取资源
    /// </summary>
    /// <param name="hashInfo">hashInfo</param>
    /// <param name="tenantId">租户ID</param>
    /// <returns></returns>
    public async Task<Torrent> GetAsync(string hashInfo, long? tenantId)
    {
        return await _torrentsRepository.Where(u => u.HashInfo == hashInfo && u.TenantId == tenantId, ignoreQueryFilters: true).FirstOrDefaultAsync();
    }

    /// <summary>
    /// 分页
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<PageResult<TorrentOutput>> QueryTorrentPageList([FromQuery] TorrentPageInput input)
    {
        var title = !string.IsNullOrEmpty(input.Title?.Trim());
        var subtitle = !string.IsNullOrEmpty(input.Subtitle?.Trim());
        var pageResult = await _torrentsRepository.DetachedEntities
                                           .Where(title, u => EF.Functions.Like(u.Title, $"%{input.Title.Trim()}%"))
                                           .Where(subtitle, u => EF.Functions.Like(u.Subtitle, $"%{input.Subtitle.Trim()}%"))
                                           .ProjectToType<TorrentOutput>()
                                           .ToADPagedListAsync(input.PageNo, input.PageSize);
        return pageResult;
    }
    /// <summary>
    /// 修改完成做种数量
    /// </summary>
    /// <param name="hashInfo"></param>
    /// <param name="tenantId"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public async Task UpdaeCompleteCountAsync(string hashInfo, long? tenantId, int count = 1)
    {
        var torrent = await _torrentsRepository.Where(p => p.HashInfo == hashInfo && p.TenantId == tenantId, ignoreQueryFilters: true).FirstOrDefaultAsync();
        if (torrent == null) return;
        if (count > 0)
        {
            torrent.Complete += count;
            torrent.Completed += count;
            await torrent.UpdateExcludeAsync(new[] { nameof(Torrent.Complete), nameof(Torrent.Completed) }, true);
        }
        else
        {
            torrent.Complete += count;
            await torrent.UpdateExcludeAsync(new[] { nameof(Torrent.Complete) }, true);
        }
    }
    /// <summary>
    /// 修改未完成的做种数
    /// </summary>
    /// <param name="hashInfo"></param>
    /// <param name="tenantId"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public async Task UpdaeInCompleteCountAsync(string hashInfo, long? tenantId, int count = 1)
    {
        var torrent = await _torrentsRepository.Where(p => p.HashInfo == hashInfo && p.TenantId == tenantId, ignoreQueryFilters: true).FirstOrDefaultAsync();
        if (torrent == null) return;

        torrent.InComplete += count;
        await torrent.UpdateExcludeAsync(new[] { nameof(Torrent.InComplete) }, true);
    }



    /// <summary>
    /// 下载种子
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task<IActionResult> DownloadTorrent([FromQuery] QueryFileInfoInput input)
    {
        var file = await _sysFileService.GetFileInfo(input);
        var fileName = $"{file.FileOriginName}";//HttpUtility.UrlEncode(file.FileOriginName, Encoding.GetEncoding("UTF-8"));

        string passKey = App.User?.FindFirst(ClaimConst.PASS_KEY)?.Value;
        string url = $"{App.HttpContext.Request.Scheme}://{App.HttpContext.Request.Host}/announce?passkey={passKey}"; //TODO 读取配置文件

        switch (file.FileLocation)
        {
            case (int)FileLocation.ALIYUN:
                var filePath = string.Concat(file.FilePath, "/", file.FileObjectName);
                var stream1 = await (await _oSSServiceFactory.Create("Aliyun").PresignedGetObjectAsync(bucketName, filePath, 5)).GetAsStreamAsync();

                var torrent = new BencodeParser().Parse<BencodeNET.Torrents.Torrent>(stream1);
                torrent.Trackers = new List<IList<string>> { new List<string> { url } };

                return new FileStreamResult(new MemoryStream(torrent.EncodeAsBytes()), "application/octet-stream") { FileDownloadName = fileName };

            case (int)FileLocation.TENCENT:
                var filePath1 = string.Concat(file.FilePath, "/", file.FileObjectName);
                var stream2 = await (await _oSSServiceFactory.Create("QCloud").PresignedGetObjectAsync(bucketName, filePath1, 5)).GetAsStreamAsync();

                var torrent2 = new BencodeParser().Parse<BencodeNET.Torrents.Torrent>(stream2);
                torrent2.Trackers = new List<IList<string>> { new List<string> { url } };

                return new FileStreamResult(new MemoryStream(torrent2.EncodeAsBytes()), "application/octet-stream") { FileDownloadName = fileName };

            case (int)FileLocation.MINIO:
                var filePath2 = string.Concat(file.FilePath, "/", file.FileObjectName);
                var stream3 = await (await _oSSServiceFactory.Create().PresignedGetObjectAsync(file.FileBucket, filePath2, 5)).GetAsStreamAsync();

                var torrent3 = new BencodeParser().Parse<BencodeNET.Torrents.Torrent>(stream3);
                torrent3.Trackers = new List<IList<string>> { new List<string> { url } };

                return new FileStreamResult(new MemoryStream(torrent3.EncodeAsBytes()), "application/octet-stream") { FileDownloadName = fileName };

            default:
                var filePath4 = Path.Combine(file.FilePath, file.FileObjectName);
                var path = Path.Combine(App.WebHostEnvironment.WebRootPath, filePath4);

                var torrent4 = new BencodeParser().Parse<BencodeNET.Torrents.Torrent>(path);
                torrent4.Trackers = new List<IList<string>> { new List<string> { url } };

                return new FileStreamResult(new MemoryStream(torrent4.EncodeAsBytes()), "application/octet-stream") { FileDownloadName = fileName };
        }
    }
}
